package pathways;

/**
 * Title:        Pathways
 * Description:  Pure Java version of the Pathways Project.
 * Copyright:    Copyright (c) 2002
 * Company:      Ants Technologies
 * @author Veejay Sani
 * @version 1.0
 */

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.geom.AffineTransform;

public class CEnzymePainter implements IEnzymePainter
{
  public CEnzymePainter()
  {
    arrowHeadAltitude = 10;
    arrowBaseWidth = 2*arrowHeadAltitude;
  }

  public void draw(Graphics g, CEnzyme e)
  {
    // draw line between two points
    // draw arrow head
    // write text

    if (e.isVisible())
    {
      Point p1 = e.getPoint1();
      Point p2 = e.getPoint2();
      int connectingTo = e.getConnectingTo();
      int headAltitude = arrowHeadAltitude;
      int basewidth = arrowBaseWidth;
      int xOffset = 0;
      int yOffset = 0;

      if (connectingTo == 0)
        xOffset = headAltitude;
      else if (connectingTo == 1)
        yOffset = headAltitude;
      else if (connectingTo == 2)
        xOffset = -headAltitude;
      else if (connectingTo == 3)
        yOffset = -headAltitude;

      Point baseOfArrowHead = new Point(p2.x-xOffset,p2.y-yOffset);
      
      // draw line
      g.drawLine(p1.x,p1.y,baseOfArrowHead.x,baseOfArrowHead.y);

      // draw Arrow Head - tip at p2
      drawArrowHead(g,p2,headAltitude,basewidth,connectingTo);

      // write the text
      drawText(e.getName(),p1,baseOfArrowHead,Color.blue,g);

      // testing -- drawing polygon
      Color co = g.getColor();
      g.setColor(Color.white);
      g.drawPolygon((Polygon)e.getShape());
      g.setColor(co);
    }
  }

  private void drawArrowHead(Graphics g,Point peak, int altitude, int basewidth, int orientation)
  {
    Point[] points = computeEdgesOfArrowHead(peak,altitude,basewidth,orientation);

    Point edge1 = points[0];
    Point edge2 = points[1];

    int x[] = new int[3];
    x[0] = peak.x;
    x[1] = edge1.x;
    x[2] = edge2.x;

    int y[] = new int[3];
    y[0] = peak.y;
    y[1] = edge1.y;
    y[2] = edge2.y;

    g.fillPolygon(x,y,3);
  }

  private Point[] computeEdgesOfArrowHead(Point peak, int altitude, int basewidth, int orientation)
  {
    Point[] points = new Point[2];
    points[0] = new Point();
    points[1] = new Point();

    Point edge1 = points[0];
    Point edge2 = points[1];

    if (orientation == 0)
    {
      edge1.x = peak.x - altitude;
      edge1.y = peak.y - basewidth/2;

      edge2.x = edge1.x;
      edge2.y = peak.y + basewidth/2;
    }
    else if (orientation == 1)
    {
      edge1.y = peak.y - altitude;
      edge1.x = peak.x - basewidth/2;

      edge2.y = edge1.y;
      edge2.x = peak.x + basewidth/2;
    }
    else if (orientation == 2)
    {
      edge1.x = peak.x + altitude;
      edge1.y = peak.y - basewidth/2;

      edge2.x = edge1.x;
      edge2.y = peak.y + basewidth/2;
    }
    else
    {
      edge1.y = peak.y + altitude;
      edge1.x = peak.x - basewidth/2;

      edge2.y = edge1.y;
      edge2.x = peak.x + basewidth/2;
    }

    return points;
  }

  private void drawText(String text,Point p1, Point p2, Color color, Graphics g)
  {
    // get # of chars that are printable
    // get degrees of Affine Transform

    Point source = null;
    Point dest = null;

    // source should be the one that is to the left
    if (p2.x >= p1.x)
    {
      source = p1;
      dest = p2;
    }
    else
    {
      source = p2;
      dest = p1;
    }

    double degrees = getdegrees(source,dest);

    //System.out.println("Degrees = " + degrees);

    Font f = new Font("Verdana",0,10);
    FontMetrics fm = g.getFontMetrics(f);
    int charWidth = fm.charWidth('Z');

    double distance = source.distance(dest);
    int numOfCharsPrintable = (int)(distance/charWidth);

    String textToPrint = new String("");

    // if we can print all of it then do so else
    // print whatever we can with "..."
    // atleast print "..."
    if (text.length() <= numOfCharsPrintable)
    {
      textToPrint = text;
    }
    else
    {
      if (numOfCharsPrintable > 3)
        textToPrint = text.substring(0,numOfCharsPrintable-3);

      textToPrint = textToPrint + "...";
    }
    
    // write a string from the source to the destination (left to right)
    printString(textToPrint,source,degrees,f,color,g);
  }

  private double getdegrees(Point p1, Point p2)
  {
    // computes degrees from p1 to p2
    // assumes p2.x >= p1.x

    int dx = p2.x - p1.x;
    int dy = p2.y - p1.y;
    double absdy = Math.abs(dy);
    double degrees = 0;

    if (dx == 0)
    {
      degrees = 90.0;
    }
    else
    {
      //System.out.println("P1 = " + p1.toString() + " P2 = " + p2.toString());
      //System.out.println("atan(" + absdy/dx + ")");

      degrees = Math.abs(Math.toDegrees(Math.atan(absdy/dx)));
    }

    if (dy < 0)
    {
      degrees = -degrees;
    }

    return degrees;
  }

  private void printString(String text,Point p, double degrees, Font font, Color color, Graphics g)
  {
    Font f = g.getFont();
    Color c = g.getColor();
    AffineTransform rotation = AffineTransform.getRotateInstance(Math.toRadians(degrees));
    Font rotatedFont = f.deriveFont(rotation);

    g.setColor(color);
    g.setFont(rotatedFont);
    g.drawString(text,p.x,p.y);
    g.setFont(f);
    g.setColor(c);
  }

  public Shape getEnclosingShape(CEnzyme e)
  {
    return computePolygon(e);
  }

  private Polygon computePolygon(CEnzyme e)
  {
    int[] x = new int[4];
    int[] y = new int[4];
    int connectingTo = e.getConnectingTo();
    Point p1 = e.getPoint1();
    Point p2 = e.getPoint2();

    Point[] points = computeEdgesOfArrowHead(p2,arrowHeadAltitude,arrowBaseWidth,connectingTo);

    Point edge1 = points[0];
    Point edge2 = points[1];

    int polygonSideHalfSize = 10;

    // computes the enclosing polygon and returns it
    // 0 & 1 are points near the head
    if (connectingTo == 0 || connectingTo == 2)
    {
      x[0] = edge1.x;
      x[1] = edge2.x;
      y[0] = edge1.y;
      y[1] = edge2.y;

      x[2] = p1.x;
      x[3] = p1.x;
      y[2] = p1.y + polygonSideHalfSize;
      y[3] = p1.y - polygonSideHalfSize;
    }
    else
    {
    /*
      x[0] = p2.x - polygonSideHalfSize;
      x[1] = p2.x + polygonSideHalfSize;
      y[0] = p2.y;
      y[1] = p2.y;
    */

      x[0] = edge1.x;
      x[1] = edge2.x;
      y[0] = edge1.y;
      y[1] = edge2.y;

      x[2] = p1.x + polygonSideHalfSize;
      x[3] = p1.x - polygonSideHalfSize;
      y[2] = p1.y;
      y[3] = p1.y;
    }

    return new Polygon(x,y,4);
  }

  private int arrowHeadAltitude;
  private int arrowBaseWidth;
}